# -*- coding: utf-8 -*-
"""
Created on Sat Oct 19 09:54:23 2019

@author: shosborn
"""

#!/usr/bin/python3

import RTCSA_runScenario as scenario
import RTCSA_TaskSystem2 as taskSystem
import os.path
from multiprocessing.pool import ThreadPool
import sys

CORE_COUNT = int(sys.argv[1])
#EXPECTED_SCORE = float(sys.argv[2])
SLOPE = float(sys.argv[2])
SCORE_DISTRO = int(sys.argv[3])
PERIOD_COUNT = int(sys.argv[4])
MIN_SAMPLES = int(sys.argv[5])
FILE_OUT = sys.argv[6]



# testing parameters
# CORE_COUNT = 2
# EXPECTED_SCORE = 0.55
# SLOPE = 0.0
# SCORE_DISTRO = 0
# PERIOD_COUNT = 4
# MIN_SAMPLES = 5
# FILE_OUT = "rtcsa_test_out.csv"

#FILE_OUT = "test.csv"
FIXED = taskSystem.FIXED
EXPO = taskSystem.EXPO
PERIOD_MIN = 10

BIN_SIZE=.25
UTIL_RANGES=[(0, .4), (.3, .7), (.6, 1), (0, 1)] #low, medium, high, wide
PROB_HARM = 0
DEGREE_HARM = 2
EXPECTED_SCORES = (0.35, 0.55, 0.75)

NO_PREEMPTION=taskSystem.NO_PREEMPTION
DOUBLE_COST=taskSystem.DOUBLE_COST
DATA_DRIVEN=taskSystem.DATA_DRIVEN
PREEMPTION_POINT=taskSystem.PREEMPTION_POINT
FULL_PREEMPTION=taskSystem.FULL_PREEMPTION

BASELINE=-1

TIMEOUT = 60


MAX_NUMBER_BINS=int( (2*16-16/2)/BIN_SIZE +1 )


# Whatever is covered by this (nested) loop will come out as one file.

configurations = []

for util_range in UTIL_RANGES:
    for expected_score in EXPECTED_SCORES:
        config = {}
        config["coreCount"] = CORE_COUNT
        config["minTotalUtil"] = CORE_COUNT/2-BIN_SIZE
        config["maxTotalUtil"] = CORE_COUNT*2
        config["bin_size"] = BIN_SIZE
        config["min_samples"] = MIN_SAMPLES
        config["minTaskUtil"] = util_range[0]
        config["maxTaskUtil"] = util_range[1]
        config["expected_score"] = expected_score
        config["slope"] = SLOPE
        config["prob_harm"] = PROB_HARM
        config["degree_harm"] = DEGREE_HARM
        config["score_distro"] = SCORE_DISTRO
        config["periodCount"] = PERIOD_COUNT
        config["periodMin"] = 10
        config["solutionLimit"] = 1000
        config["lowerBound"] = 1
        config["upperBound"] = 1
        config["timeout"] = TIMEOUT
        configurations.append(config)

# Run mode configuration
SYNC = False


#change to match my parameters

#the input to run_test, 'config', is one of the many dictionaries contained in the list 'configurations'
def run_test(config):
    #the '**' notation indicates that dictionary config is to be treated as a set of keyword arguments
    (theBins, runtime)=scenario.runParamTest(**config)
    save_results(config, theBins, runtime)
    

def save_results(config, theBins, runtime):
    with open(FILE_OUT, "a") as f:
       
        #BASELINE=-1
        #see comment in resultsAlgAndModel for explanation of double loop
        for alg in range(BASELINE, taskSystem.NUM_PARTITION_ALGS):
            for model in range(taskSystem.NUM_INNER_COST_MODELS):
                if alg==BASELINE:
                    model=-1 #since innerCostModel isn't applicable w/o SMT
                (resultsByScenario, relSchedArea, extraArea)=resultsAlgAndModel(alg, model, theBins, CORE_COUNT, config["bin_size"], config["minTotalUtil"], MAX_NUMBER_BINS)
                #print scenario parameters
                print(config["coreCount"], config["minTaskUtil"], config["maxTaskUtil"], config['periodCount'],
                      config['expected_score'], config['score_distro'], config['slope'], config['timeout'], runtime,
                      BIN_SIZE, MIN_SAMPLES, alg, model, sep=',', end=",", file=f)
                #print RSA and extra area
                print(relSchedArea, extraArea, sep=',', end=',', file=f )
                #print results
                for r in resultsByScenario:
                    print(r, end=',', file=f)
                print(file=f)
                if alg==BASELINE:
                    #model is irrelevant here
                    break


'''
Each call to run test actually returns a double-list theBins that covers
21 cases; the baseline plus 4 bin-packing algs * 5 inner cost models.
Each of these cases needs its own row; hence 21 rows at a time.
        
Results is a nested list; the outer list contains one inner list per bin;
each inner list contains 22 entries.
Entry 0 is number of samples in that bin.
Entry 1 is the number of baseline successes in that bin.
Entry 2+alg*NUM_INNER_COST_MODELS+model gives number of success for
the given inner cost model.
Some list manipulation is required;
each printed output row is based on two entries (successes and numSamples)
from lists corresponding to each utilization bin.       
''' 
def resultsAlgAndModel(alg, model, theBins, coreCount, binSize, minUtil, maxNumberBins):
    theseResults=[]
    length=len(theBins)
    area=0
    extraArea=0
    #print(theBins)
    for thisBin in range(length):
        numSamples=theBins[thisBin][0]
        if alg==BASELINE:
            successCount=theBins[thisBin][1]
        else:
            successCount=theBins[thisBin][2+(alg*taskSystem.NUM_INNER_COST_MODELS+model)]
        if numSamples==0:
            theseResults.append("NA")
        else:
            theseResults.append(successCount/numSamples)
            '''
            binRange=[minUtil+thisBin*binSize, minUtil+(thisBin+1)*binSize)
            '''
            if thisBin==0:
                '''
                minUtil gives the smallest util that's large enough;
                samples in first bin will fall in range [minUtil, minUtil+binSize)
                '''
                area=(successCount/numSamples)*(minUtil+binSize/2)
            else:
                area += (successCount/numSamples)*binSize
            if minUtil+thisBin*binSize>coreCount:
                extraArea+=(successCount/numSamples)*binSize
            
	#pad theseResults with zeroes so that list will be the same length for all
    for pretendBin in range(length, maxNumberBins):
        theseResults.append(0)
    return (theseResults, area/coreCount, extraArea)

# We need this protected so that subprocesses do not run it
if __name__ == '__main__':
    with open(FILE_OUT, "a") as f:
        print("coreCount,minTaskUtil,maxTaskUtil,numPeriods,expected_score,score_distro,slope,timeout,runtime,binSize,minSamples,alg,model,RSA,extraArea,results", file=f)
    # Use multithreading
    
    with ThreadPool(processes=len(configurations)) as pool:
        # If we do not need the results to be in order, save things whenever we get them
        for res_tup in pool.imap_unordered(run_test, configurations):
            pass
